<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title></title>
    <script type="text/javascript" src="./js/vue.js"></script>
    <script type="text/javascript" src="./js/json2.js"></script>
    <script type="text/javascript" src="./js/jsonlint.js"></script>
    <script type="text/javascript" src="./js/axios.min.js"></script>
    <script type="text/javascript" src="./js/codemirror.min.js"></script>
    <script type="text/javascript" src="./js/vue-codemirror.js"></script>
    <script type="text/javascript" src="./js/javascript/javascript.js"></script>
    <script type="text/javascript" src="./js/fold/brace-fold.js"></script>
    <script type="text/javascript" src="./js/fold/comment-fold.js"></script>
    <script type="text/javascript" src="./js/fold/foldcode.js"></script>
    <script type="text/javascript" src="./js/fold/foldgutter.js"></script>
    <script type="text/javascript" src="./js/fold/indent-fold.js"></script>
    <script type="text/javascript" src="./js/fold/markdown-fold.js"></script>
    <script type="text/javascript" src="./js/fold/xml-fold.js"></script>
    <script type="text/javascript" src="./js/jquery.min.js"></script>
    <script type="text/javascript" src="./js/crypto-js.min.js"></script>
    <script src="./js/mergely.min.js"></script>
    <link href="./js/fold/foldgutter.css" rel="stylesheet">
    <link href="./css/theme/eclipse.css" rel="stylesheet">
    <link href="./css/codemirror.min.css" rel="stylesheet">
    <link type="text/css" rel="stylesheet" href="./css/mergely.css"/>
</head>
<body>
<div id="app">
    <div class="header">
        <h1>开发者工具</h1>
    </div>
    <div class="container">
        <div class="c_left">
            <ul type="none" class="c_left_nav">
                <li :class="divId=='jsonPretty'?'li_selected':'li_nonselected'"><a @click="changeDiv('jsonPretty')">json验证</a>
                <li :class="divId=='textDiff'?'li_selected':'li_nonselected'"><a
                        @click="changeDiv('textDiff')">文本比对</a>
                <li :class="divId=='createImage'?'li_selected':'li_nonselected'"><a @click="changeDiv('createImage')">图片生成</a>
                </li>
                <li :class="divId=='ipRegion'?'li_selected':'li_nonselected'"><a
                        @click="changeDiv('ipRegion')">ip归属地</a></li>
                <li :class="divId=='genUuid'?'li_selected':'li_nonselected'"><a
                        @click="changeDiv('genUuid')">uuid生成</a>
                </li>
                <li :class="divId=='md5'?'li_selected':'li_nonselected'"><a @click="changeDiv('md5')">md5加密</a></li>
                <li :class="divId=='timeConvert'?'li_selected':'li_nonselected'"><a @click="changeDiv('timeConvert')">时间转换</a>
                </li>
            </ul>
        </div>

        <div class="c_right">
            <div id="jsonPretty" v-show="divId=='jsonPretty'" style="display: flex;justify-content: center;">
                <div class="json_pretty_input">
                    <textarea class="text_area" placeholder="请输入json字符" v-model="jsonInput" @blur="jsonPretty"
                              @keyup="jsonPretty" @change="jsonPretty"></textarea>
                </div>
                <div class="json_pretty_output">
                    <local-codemirror class="text_area" ref="jsonOutputRef" v-model="jsonOutput" :options="jsonOption">
                    </local-codemirror>
                </div>
            </div>
            <div id="textDiff" v-show="divId=='textDiff'" style="width: 90%;height:680px;text-align: left">
                <div id="textDiffDiv"></div>
            </div>
            <div id="genUuid" v-show="divId=='genUuid'">
                <input type="number" maxlength="3" class="text_input" v-model="uuidCount" placeholder="生成uuid的个数"
                       min="1" max="100"/>
                <button @click="doGenUuid()" class="bttn">生成</button>
                <div style="margin-top: 15px" v-show="uuids!==undefined && uuids.length > 0">
                    <label style="font-size: 20px;font-weight: lighter">已为您生成了{{uuids.length}}个uuid:</label>
                </div>
                <div class="input_result" style="border: 1px solid #dee2e6;color: red;font-size: 18px"
                     v-show="uuids!==undefined && uuids.length > 0">
                    <template v-for="uuid in uuids">
                        {{uuid}}<br>
                    </template>
                </div>
            </div>
            <div id="md5" v-show="divId=='md5'">
                <input type="text" maxlength="30" class="text_input" v-model="md5Text" placeholder="输入加密字符串"/>
                <button @click="doMd5()" class="bttn">加密</button>
                <div class="input_result" v-if="md5Result!=''">
                    <label style="font-size: 20px;font-weight: lighter">加密结果:</label>
                    <span>{{md5Result}}</span>
                </div>
                <div class="input_result" v-if="md5Error!=''">
                    <span>{{md5Error}}</span>
                </div>
            </div>
            <div id="ipRegion" v-show="divId=='ipRegion'">
                <input type="text" maxlength="100" class="text_input" v-model="ipText" placeholder="输入查询ip地址"/>
                <button @click="doIpRegion()" class="bttn">查询</button>
                <div class="input_result" v-if="ipRegionResult!=''">
                    <label style="font-size: 20px;font-weight: lighter">ip归属地:</label>
                    <span>{{ipRegionResult}}</span>
                </div>
                <div class="input_result" v-if="ipRegionError!=''">
                    <span>{{ipRegionError}}</span>
                </div>
            </div>
            <div id="createImage" v-show="divId=='createImage'">
                <div style="position: relative;display: inline-block">
                    <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">背景色:</label>
                    <input type="color" v-model="imageColor" class="number_input"/>
                </div>

                <div style="position: relative;display: inline-block">
                    <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">长度:</label>
                    <input type="number" maxlength="4" step="100" class="number_input" v-model="imageWidth"
                           placeholder="图片长度" min="100" max="2000" style="position: relative;display: inline-block">
                    <span class="input_tipspan" v-show="imageWidthError!=''">{{imageWidthError}}</span>
                </div>

                <div style="position: relative;display: inline-block">
                    <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">高度:</label>
                    <input type="number" maxlength="4" step="100" class="number_input" v-model="imageHeight"
                           placeholder="图片高度" min="100" max="2000"/>
                    <span class="input_tipspan" v-show="imageHeightError!=''">{{imageHeightError}}</span>
                </div>

                <div style="position: relative;display: inline-block">
                    <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">大小:</label>
                    <input type="number" maxlength="3" class="number_input" v-model="imageMB" placeholder="图片大小MB"
                           min="1" max="200"/>
                    <span class="input_tipspan" v-show="imageMBError!=''">{{imageMBError}}</span>
                </div>

                <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">格式:</label>
                <select class="image_select" v-model="imageType">
                    <option value="jpg">jpg</option>
                    <option value="jpeg">jpeg</option>
                    <option value="png">png</option>
                </select>
                <button @click="doViewImage()" class="bttn">预览</button>
                <button @click="doCreateImage(1)" class="bttn">下载</button>
                <div class="download_batch_image">
                    <button @click="doCreateImage(5)" class="bttn">下载5个</button>
                    <button @click="doCreateImage(10)" class="bttn">下载10个</button>
                    <button @click="doCreateImage(20)" class="bttn">下载20个</button>
                </div>
                <div class="input_result" v-if="createImageResult!=''">
                    <img :src="createImageResult" style="border-color: red;border: 2px solid red"/>
                </div>
                <div class="input_result" v-if="createImageError!=''">
                    <span>{{createImageError}}</span>
                </div>
            </div>
            <div id="timeConvert" v-show="divId=='timeConvert'">
                <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">当前时间戳(秒):</label>
                <span style="color: red;background-color: black">{{nowSeconds}}</span>
                <label style="padding-left: 25px;padding-right: 5px;font-size: 20px">当前时间戳(毫秒):</label>
                <span style="color: red;background-color: black">{{nowMillSeconds}}</span>
                <hr>
                <table style="margin: 0 auto;">
                    <tr>
                        <td><label>时间</label></td>
                        <td><input type="text" v-model="inputDate"></td>
                        <td>
                            <button @click="date2Mills()">转换</button>
                        </td>
                        <td><input type="text" v-model="inputDateMills"></td>
                        <td><label>毫秒</label></td>
                    </tr>
                    <tr>
                        <td><label>时间</label></td>
                        <td><input type="text" v-model="inputDate"></td>
                        <td>
                            <button @click="date2Seconds()">转换</button>
                        </td>
                        <td><input type="text" v-model="inputDateSeconds"></td>
                        <td><label>秒</label></td>
                    </tr>
                    <tr>
                        <td><label>时间戳(秒)</label></td>
                        <td><input type="text" v-model="inputSeconds"></td>
                        <td>
                            <button @click="seconds2Date()">转换</button>
                        </td>
                        <td><input type="text" v-model="inputSecondsDate"></td>
                    </tr>
                    <tr>
                        <td><label>时间戳(毫秒)</label></td>
                        <td><input type="text" v-model="inputMills"></td>
                        <td>
                            <button @click="mills2Date()">转换</button>
                        </td>
                        <td><input type="text" v-model="inputMillsDate"></td>
                    </tr>
                </table>
            </div>
        </div>

    </div>

</div>
</body>
<script type="module">
    const server_url = "http://localhost:8555";
    Vue.use(VueCodemirror)
    var vm = new Vue({
        el: "#app",
        data: {
            divId: '',
            md5Text: '',
            ipText: '',
            imageWidth: '1000',
            imageWidthError: '',
            imageHeight: '680',
            imageHeightError: '',
            imageMB: '5',
            imageMBError: '',
            imageType: 'jpg',
            uuidCount: '',
            uuids: [],
            md5Result: '',
            md5Error: '',
            createImageUri: '',
            ipRegionResult: '',
            ipRegionError: '',
            nowSeconds: '',
            nowMillSeconds: '',
            inputDate: '',
            inputDateMills: '',
            inputDateSeconds: '',
            inputSeconds: '',
            inputMills: '',
            inputSecondsDate: '',
            inputMillsDate: '',
            imageColor: '#FFFFFF',
            createImageResult: '',
            createImageError: '',
            jsonInput: '',
            jsonOutput: '',
            jsonOption: {
                theme: 'eclipse',
                indentWithTab: true,
                tabSize: 2,
                autofocus: true,
                language: 'javascript',
                lineNumbers: true,
                lineWrapping: true,
                foldGutter: {
                    rangeFinder: new CodeMirror.fold.combine(CodeMirror.fold.indent, CodeMirror.fold.comment)
                },
                gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
            }
        },
        components: {
            LocalCodemirror: VueCodemirror.codemirror
        },
        mounted() {
            var current = new Date();
            this.inputDate = this.formatDate(current);
            this.inputMills = current.getTime();
            this.inputSeconds = Math.round(current.getTime() / 1000);
            this.changeDiv('jsonPretty');
            this.startTimer();
        },
        methods: {
            startTimer() {
                setInterval(() => {
                    this.nowMillSeconds = new Date().getTime();
                    this.nowSeconds = Math.round(new Date().getTime() / 1000);
                }, 1000);
            },
            isIp(ip) {
                var ip_arr = ip.split('.');
                if (ip_arr.length != 4) {
                    return false;
                }
                for (var i = 0; i < ip_arr.length; i++) {
                    if (ip_arr[i] < 0 || ip_arr[i] > 255) {
                        return false;
                    }
                }
                return true;
            },
            jsonPretty() {
                this.jsonOutput = '';
                if (this.jsonInput != '') {
                    try {
                        var result = jsonlint.parse(this.jsonInput);
                        if (result) {
                            this.jsonOutput = JSON.stringify(result, null, "  ");
                        } else {
                            this.jsonOutput = 'json valid error';
                        }
                    } catch (e) {
                        this.jsonOutput = e.toString();
                    }
                }
            },
            changeDiv(divId) {
                this.divId = divId;
                setTimeout(() => {
                    console.log(this.divId)
                    if (this.divId == 'textDiff') {
                        $(document).ready(function () {
                            $('#textDiffDiv').mergely({
                                lhs: function (setValue) {
                                    setValue('the quick red fox\njumped over the hairy dog');
                                },
                                rhs: function (setValue) {
                                    setValue('the quick brown fox\njumped over the lazy dog');
                                },
                                editor_width: '300px',
                                editor_height: '200px',
                            });
                        });
                    }
                });

            },
            doGenUuid() {
                this.uuids.length=0;
                if (this.uuidCount == '' || this.uuidCount > 100) {
                    this.uuidCount = 100;
                }
                for (var i = 0; i < this.uuidCount; i++) {
                    this.uuids.push(this.genUuid());
                }
            },
            genUuid() {
                return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
                    return v.toString(16);
                });
            },
            doMd5() {
                this.md5Result = '';
                this.md5Error = '';
                if (this.md5Text == '') {
                    this.md5Error = '加密字符串不能为空';
                    return;
                }
                this.md5Result = window.CryptoJS.MD5(this.md5Text).toString();
            },
            doIpRegion() {
                this.ipRegionResult = '';
                this.ipRegionError = '';
                if (this.ipText == '') {
                    this.ipRegionError = '请输入ip地址';
                    return;
                } else {
                    if (!this.isIp(this.ipText)) {
                        this.ipRegionError = 'ip地址不合法';
                        return;
                    }
                }
                axios.get(server_url + '/tool/ipRegion?ip=' + this.ipText)
                    .then((response) => {
                        if (response.status == 200) {
                            if (response.data.code == 200) {
                                this.ipRegionResult = response.data.result;
                            } else {
                                this.ipRegionError = response.data.msg;
                            }
                        }
                    })
                    .catch((error) => {
                        this.ipRegionError = error;
                    });
            },
            validateImageParam() {
                this.imageWidthError = '';
                this.imageHeightError = '';
                this.imageMBError = '';
                var result = true;
                if (this.imageWidth == '') {
                    this.imageWidthError = '图片长度不能为空';
                    result = false;
                } else {
                    if (this.imageWidth < 0) {
                        this.imageWidthError = '图片长度不小于0';
                        result = false;
                    }
                    if (this.imageWidth > 2000) {
                        this.imageWidthError = '图片长度不能超过2000';
                        result = false;

                    }
                }
                if (this.imageHeight == '') {
                    this.imageHeightError = '图片宽度不能为空';
                    result = false;
                } else {
                    if (this.imageHeight < 0) {
                        this.imageHeightError = '图片宽度不能小于0';
                        result = false;
                    }
                    if (this.imageHeight > 2000) {
                        this.imageHeightError = '图片宽度不能超过2000';
                        result = false;
                    }
                }
                if (this.imageMB == '') {
                    this.imageMBError = '图片大小不能为空';
                    result = false;
                } else {
                    if (this.imageMB < 0) {
                        this.imageMBError = '图片大小不能小于0';
                        result = false;
                    }
                    if (this.imageMB > 200) {
                        this.imageMBError = '图片大小不能超过200MB';
                        result = false;
                    }
                }
                return result;
            },
            doViewImage() {
                if (this.validateImageParam()) {
                    this.createImageResult = '';
                    this.createImageError = '';
                    axios.get(server_url + "/tool/createImage?width=" + this.imageWidth + "&height=" + this.imageHeight + "&MB=" + this.imageMB + "&imageType=" + this.imageType + "&imageColor=" + this.imageColor.substr(1) + "&timestamp=" + new Date().getTime(), {responseType: 'blob'})
                        .then((res) => {
                            if (res.status == 200) {
                                var responseType = res.headers['content-type'];
                                if (responseType.startsWith("application/json")) {
                                    const reader = new FileReader();
                                    reader.readAsText(res.data, 'utf-8');
                                    reader.onload = () => {
                                        this.createImageError = JSON.parse(reader.result).msg;
                                    }
                                } else {
                                    const blob = new Blob([res.data], {type: responseType});
                                    const URL = window.URL || window.webkitURL;
                                    this.createImageResult = URL.createObjectURL(blob);
                                }
                            }
                        })
                        .catch((error) => {
                            this.createImageError = error;
                        });
                }
            },
            doCreateImage(imageNum) {
                if (this.validateImageParam()) {
                    this.createImageResult = '';
                    this.createImageError = '';
                    let baseUrl = "/tool/createImage?";
                    if (imageNum > 1) {
                        baseUrl = "/tool/createBatchImage?imageNum=" + imageNum + "&";
                    }
                    axios.get(server_url + baseUrl + "width=" + this.imageWidth + "&height=" + this.imageHeight + "&MB=" + this.imageMB + "&imageType=" + this.imageType + "&imageColor=" + this.imageColor.substr(1) + "&timestamp=" + new Date().getTime(), {responseType: 'blob'})
                        .then((res) => {
                            if (res.status == 200) {
                                var responseType = res.headers['content-type'];
                                if (responseType.startsWith("application/json")) {
                                    const reader = new FileReader();
                                    reader.readAsText(res.data, 'utf-8');
                                    reader.onload = () => {
                                        this.createImageError = JSON.parse(reader.result).msg;
                                    }
                                } else {
                                    var filename = "";
                                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                                    var matches = filenameRegex.exec(res.headers['content-disposition']);
                                    if (matches != null && matches[1]) {
                                        filename = matches[1].replace(/['"]/g, '');
                                    }
                                    const blob = new Blob([res.data], {type: responseType});
                                    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                                        window.navigator.msSaveOrOpenBlob(blob, filename)
                                    } else {
                                        const url = URL.createObjectURL(blob);
                                        const link = document.createElement('a');
                                        link.href = url;
                                        link.download = filename;
                                        document.body.appendChild(link);
                                        link.click();
                                        document.body.removeChild(link);
                                        URL.revokeObjectURL(url);
                                    }
                                }
                            }
                        })
                        .catch((error) => {
                            this.createImageError = error;
                        });
                }
            },
            formatDate(date) {
                console.log(date)
                const year = date.getFullYear();
                const month = (date.getMonth() + 1).toString().padStart(2, '0');
                const day = date.getDate().toString().padStart(2, '0');
                const hour = date.getHours().toString().padStart(2, '0');
                const minute = date.getMinutes().toString().padStart(2, '0');
                const second = date.getSeconds().toString().padStart(2, '0');
                const formattedDate = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
                return formattedDate
            },
            parseDate(dateStr) {
                return Date.parse(new Date(dateStr).toString());
            },
            date2Mills() {
                this.inputDateMills = this.parseDate(this.inputDate);
            },
            date2Seconds() {
                this.inputDateSeconds = Math.round(this.parseDate(this.inputDate) / 1000);
            },
            seconds2Date() {
                this.inputSecondsDate = this.formatDate(new Date(this.inputSeconds * 1000));
            },
            mills2Date() {
                this.inputMillsDate = this.formatDate(new Date(this.inputMills * 1));
            },
        }
    });
</script>
<style>
    ul {
        margin: 0;
        padding: 0;
        list-style-type: none;
    }

    .header {
        width: 100%;
        height: 70px;
        text-align: center;
    }

    .container {
        width: 100%;
        height: 680px;
        display: flex;
        justify-content: center;
    }

    .c_left {
        width: 10%;
        background-color: whitesmoke;
    }

    .c_left_nav li {
        margin-top: 18px;
    }

    .c_left_nav li a {
        text-decoration: none;
        font-size: 15px;
        font-weight: bold;
        color: black;
        margin-left: 48px;
    }

    .li_selected {
        background-color: #d2c9c9;
    }

    .c_left_nav li a:hover {
        color: #9e9edd;
        text-decoration: none;
    }

    .c_right {
        text-align: center;
        width: 80%;
    }

    .text_input {
        width: 300px;
        height: 30px;
        font-size: 16px;
        background-color: white;
    }

    .number_input {
        width: 160px;
        height: 30px;
        font-size: 16px;
        background-color: white;
    }

    .bttn {
        margin-left: 15px;
        height: 35px;
        font-size: 18px;
    }

    .download_batch_image {
        display: block;
        margin: 0 auto;
        margin-top: 15px;
    }

    .input_result {
        margin-top: 20px;
    }

    .input_result span {
        font-size: 23px;
        color: red;
        font-weight: bold;
    }

    .image_select {
        width: 140px;
        height: 35px;
        background-color: white;
        border: 2px solid;
        font-size: 16px
    }

    .input_tipspan {
        position: absolute;
        top: 40px;
        font-size: 10px;
        color: red;
        left: 80px;
    }

    .json_pretty_input, .json_pretty_output {
        width: 47%;
        height: 680px;
    }

    .json_pretty_output {
        width: 49%;
    }

    .text_area {
        font-size: 15px;
        width: 95%;
        height: 98%;
        resize: none;
        outline: none;
        text-align: left;
        border: 2px solid #eee;
    }

    .CodeMirror {
        border: none;
        height: 100%;
    }

</style>
</html>
